今天預計講解下面兩個 (也就是下圖的步驟 5)
在 Spec 第七章有詳細列出哪一種 Request 的訊息內文需要哪些東西,像是 Spec 在 OrderCreate 給的例子
{
"ShopNo": "BA0026_001",
"OrderNo": "A201804270001",
"Amount": 50000,
"CurrencyID": "TWD",
"PayType": "A",
"ATMParam": { "ExpireDate": "20180502" },
"CardParam": { },
"ConvStoreParam": { },
"PrdtName": "虛擬帳號訂單",
"ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient/Store/Return",
"BackendURL": "http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess"
}
但是實際上在 OrderCreate 必須要有的參數只有下面幾項,其他會根據你的參數不同,有不同需求~
Name | Type | Required | Note |
---|---|---|---|
ShopNo | bytes(20) | Y | 商店代號 |
OrderNo | bytes(50) | Y | 訂單編號 |
Amount | Int(9) | Y | 訂單總金額 |
CurrencyID | bytes(3) | Y | 幣別 |
PrdtName | bytes(60) | Y | 產品名稱 |
ReturnURL | bytes(255) | Y | 付款完成頁面 |
PayType | bytes(1) | Y | 收款方式 |
如果要算出訊息的 Sign,需要注意以下幾個事項。
CardParam
、ConvStoreParam
ATMParam
param1=value1¶m2=value2
的字串
Amount=50000&BackendURL=http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess&CurrencyID=TWD&OrderNo=A201804270001&PayType=A&PrdtName=虛擬帳號訂單&ReturnURL=http://10.11.22.113:8803/QPay.ApiClient/Store/Return&ShopNo=BA0026_001
下面的程式碼是要讓我們的訊息內文轉換成要加密前的形式 (圖中的內文雜湊)
def parseQueryData(msg_param):
if type(msg_param) != dict:
return
## 這裡是將傳進來的 dict 根據 key 做排序
order_message = dict(sorted(message_content.items(), key = lambda x: x[0]))
message = ''
## 這裡是看是否為空值或是多節點,然後轉換為要求的字串
for k, v in order_message.items():
if type(v) == dict or v == '':
continue
message += f"{k}={v}&"
return message[:-1]
接著只要把前一天說的 Nonce 以及 Hash ID,將他們接在一起算 SHA256 即可獲得 Sign
## msg_content 是 parseQueryData() 傳回來 value
def calcSign(msg_content, nonce, hash_id):
sign_msg = msg_content+nonce+hash_id
s = hashlib.sha256()
s.update(sign_msg.encode('utf-8'))
h = s.hexdigest()
return h.upper()
所以到目前為止,會有下面的程式碼 (部分程式碼有做更改,但基本上功能一樣~)
import requests
import json
import hashlib
shop_no = '<shop_no>'
sinopac_hash = {
'a1': '',
'a2': '',
'b1': '',
'b2': ''
}
nonce_url = 'https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Nonce'
def getNonce():
nonce_data = {
'ShopNo': shop_no
}
r = requests.post(nonce_url, json=nonce_data)
return json.loads(r.content)['Nonce']
def calcHashID():
a1 = sinopac_hash['a1']
a2 = sinopac_hash['a2']
b1 = sinopac_hash['b1']
b2 = sinopac_hash['b2']
xor1 = hex(int(a1, base=16)^int(a2, base=16))
xor2 = hex(int(b1, base=16)^int(b2, base=16))
hash_id = xor1[2:]+xor2[2:]
return hash_id.upper()
def calcIV(nonce):
s = hashlib.sha256()
s.update(nonce.encode('utf-8'))
h = s.hexdigest()
return h[-16:].upper()
def calcSign(msg_content, nonce, hash_id):
sign_msg = msg_content+nonce+hash_id
s = hashlib.sha256()
s.update(sign_msg.encode('utf-8'))
h = s.hexdigest()
return h.upper()
order_create = {
"ShopNo": shop_no,
"OrderNo": "A202109120008",
"Amount": 50000,
"CurrencyID": "TWD",
"PayType": "C",
"CardParam": {
"AutoBilling": "Y"
},
"ConvStoreParam": { },
"PrdtName": "信用卡訂單",
"ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient-Sandbox/Store/Return",
"BackendURL": "https://10.11.22.113:8803/funBIZ.ApiClient/AutoPush/PushSuccess"
}
nonce = getNonce()
hash_id = calcHashID()
iv = calcIV(nonce)
content = parseQueryData(order_create)
signature = calcSign(content, nonce, hash_id)
預告明天會利用 Python 實作 AES-CBC,敬請期待!